package services;

import helpers.ConsoleConstants;
import helpers.ConsoleUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import models.Company;
import models.Project;
import models.Resource;
import models.ResourceType;
import models.Setting;

public class ConfigurationService extends ResourceService {

	public static final String CONFIG_FILE_EXTENSION = ".py";

	private final ProjectService projectService = ProjectService.getService();
	private final SettingService settingService = SettingService.getService();

	private static ConfigurationService service;

	// Singleton pattern
	private ConfigurationService() {

	}

	public static ConfigurationService getService() {
		if (service == null)
			service = new ConfigurationService();
		return service;
	}

	public List<Resource> getAllConfigs() {
		return Resource.find.where().eq("type", ResourceType.TestConfiguration).findList();
	}

	public Resource getConfig(long configId) {
		return Resource.find.byId(configId);
	}

	public boolean configExists(long configId) {
		return getConfig(configId) != null;
	}

	/**
	 * Add Resource entity of type config in DB and persist configuration to a file system using resource path and id as a file name
	 * 
	 * @param config
	 * @param content
	 * @return
	 */
	public Resource saveConfig(Company company, Resource config, String content) {
		Resource result = config;
		if (config.getId() <= 0) {
			result = createConfig(company, config);
		} else {
			result = updateConfig(config);
		}
		storeConfigFile(company, result, content);
		return result;
	}

	/**
	 * Add Resource entity of type config in DB and persist configuration to a file system using resource path and id as a file name
	 * 
	 * @param config
	 * @param content
	 * @return
	 */
	public Resource saveConfig(Project project, Resource config, String content) {
		Resource result = config;
		if (config.getId() <= 0) {
			result = createConfig(project, config);
		} else {
			result = updateConfig(config);
		}
		storeConfigFile(project, result, content);
		return result;
	}

	private Resource createConfig(Company company, Resource config) {
		config.setCompany(company);
		return createConfig(config);
	}

	private Resource createConfig(Project project, Resource config) {
		config.setProject(project);
		return createConfig(config);
	}

	private Resource createConfig(Resource config) {
		Date currentDate = new Date();
		config.setType(ResourceType.TestConfiguration);
		config.setCreatedDate(currentDate);
		config.setLastModifiedDate(currentDate);
		config.save();
		return config;
	}

	private Resource updateConfig(Resource config) {
		config.setLastModifiedDate(new Date());
		config.save();
		return config;
	}

	/**
	 * Fetch config properties from a config file
	 * 
	 * @param config
	 * @return
	 */
	public String getConfigFileContent(Resource config) {
		String content = "";
		if (config != null && config.getResourcePath() != null) {
			File configFile = new File(config.getResourcePath());
			if (configFile.exists()) {
				BufferedReader reader = null;
				try {
					reader = new BufferedReader(new FileReader(configFile));
					StringBuilder builder = new StringBuilder();
					String line = reader.readLine();

					while (line != null) {
						builder.append(line).append("\n");
						line = reader.readLine();
					}
					content = builder.toString();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					if (reader != null) {
						try {
							reader.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}
		return content;
	}

	/**
	 * Persist config to config file
	 * 
	 * @param content
	 * @param config
	 */
	public void storeConfigFile(final Company company, Resource config, String content) {
		if (company != null && config != null) {
			BufferedWriter writer = null;
			try {
				File configFile = null;
				if (config.getResourcePath() == null) {
					File directory = new File(getConfigDirectory(company));
					if (!directory.exists()) {
						directory.mkdirs();
					}

					configFile = new File(directory, getConfigFileName(config));

					config.setResourcePath(configFile.getAbsolutePath());
					config.save();
				} else {
					configFile = new File(config.getResourcePath());
				}
				if (configFile.exists()) {
					// Keep backup one last revision only
					File backupFile = new File(configFile.getAbsolutePath() + ".bak");
					if (backupFile.exists()) {
						backupFile.delete();
					}
					configFile.renameTo(backupFile);
				}
				writer = new BufferedWriter(new FileWriter(configFile));
				if (content != null) {
					content = content.replaceAll("\r\n|\r", "\n");
				}
				writer.write(content);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * Persist config to config file
	 * 
	 * @param content
	 * @param config
	 */
	public void storeConfigFile(Project project, Resource config, String content) {
		if (project != null && config != null) {
			BufferedWriter writer = null;
			try {
				File configFile = null;
				if (config.getResourcePath() == null) {
					File directory = new File(getConfigDirectory(project));
					if (!directory.exists()) {
						directory.mkdirs();
					}

					configFile = new File(directory, getConfigFileName(config));

					config.setResourcePath(configFile.getAbsolutePath());
					config.save();
				} else {
					configFile = new File(config.getResourcePath());
				}
				if (configFile.exists()) {
					// Keep backup one last revision only
					File backupFile = new File(configFile.getAbsolutePath() + ".bak");
					if (backupFile.exists()) {
						backupFile.delete();
					}
					configFile.renameTo(backupFile);
				}
				writer = new BufferedWriter(new FileWriter(configFile));
				if (content != null) {
					content = content.replaceAll("\r\n|\r", "\n");
				}
				writer.write(content);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

	private String getConfigFileName(Resource config) {
		return config.getId() + CONFIG_FILE_EXTENSION;
	}

	public Resource getConfig(Company company, String configName) {
		List<Resource> configs = Resource.find.where().eq("company.id", company.getId()).eq("name", configName).eq("type", ResourceType.TestConfiguration).findList();

		if (configs == null || configs.isEmpty())
			return null;
		else {
			// FIXME choose the one without having no relation with other projects
			return configs.get(0);
		}

	}

	public Resource getConfig(Project project, String configName) {
		return Resource.findResourceByProject(project, ResourceType.TestConfiguration, configName);
	}

	public String getConfigDirectory(Company company) {
		String directory = null;
		if (company != null) {
			Setting setting = settingService.getSetting(Setting.CUSTOMERS_ROOT_DIRECTORY);
			if (setting != null) {
				directory = ConsoleUtils.getPath(new String[] { setting.getStringValue(), company.getCompanyId(), ConsoleConstants.CONFIGURATION_DIRECTORY });
			}
		}
		return directory;
	}

	public String getConfigDirectory(Project project) {
		String directory = null;
		if (project != null) {
			Setting setting = settingService.getSetting(Setting.CUSTOMERS_ROOT_DIRECTORY);
			if (setting != null) {
				directory = ConsoleUtils.getPath(new String[] { projectService.getProjectDirectory(project), ConsoleConstants.CONFIGURATION_DIRECTORY });
			}
		}
		return directory;
	}
}
